using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Win32;

namespace Universe.Farm.Builtin.LocalComputerListener
{
    [Serializable]
    public class ComputerInfoDescription
    {
        [XmlAttribute("ProcNumber")]
        public int NumberOfProcessors;

        [XmlAttribute("RAM")]
        public int PhysicalMemory;

        [XmlAttribute]
        public string ProcessorName;

        [XmlAttribute("OS_Name")]
        public string OsName;
        
        [XmlIgnore]
        public Version OsVersion;

        [XmlAttribute("SP_Name")]
        public string OsServicePackName;
        
        [XmlIgnore]
        public Version OsServicePackVersion;

        public VideoControllerInfo[] VideoControllers = new VideoControllerInfo[0];

        public DotNetInfoCollection DotNetList;

        [XmlAttribute("OS_Ver")]
        public string OsVersionAsString
        {
            get { return OsVersion.ToString(); }
            set { OsVersion = new Version(value); }
        }

        [XmlAttribute("SP_Ver")]
        public string OsServicePackVersionAsString
        {
            get { return OsServicePackVersion.ToString(); }
            set { OsServicePackVersion = new Version(value); }
        }


        public static ComputerInfoDescription Current
        {
            get
            {
                ComputerInfoDescription ret = new ComputerInfoDescription();

                ulong mem = 0;
                const string
                    nameMEM = "TotalVisibleMemorySize",
                    nameSP = "CSDVersion",
                    nameSP1 = "ServicePackMajorVersion",
                    nameSP2 = "ServicePackMinorVersion",
                    nameOsName = "Caption",
                    nameOsVersion = "Version";


                // SelectQuery query = new SelectQuery("Select TotalVisibleMemorySize From Win32_OperatingSystem");
                SelectQuery query = new SelectQuery("Win32_OperatingSystem");
                    
                query.SelectedProperties.Add(nameMEM);
                query.SelectedProperties.Add(nameSP);
                query.SelectedProperties.Add(nameSP1);
                query.SelectedProperties.Add(nameSP2);
                query.SelectedProperties.Add(nameOsName);
                query.SelectedProperties.Add(nameOsVersion);

                using (ManagementObjectSearcher search = new ManagementObjectSearcher(query))
                {
                    foreach (ManagementBaseObject o in search.Get())
                    {
                        object traw1 = o[nameMEM];
                        if (traw1 != null)
                        {
                            mem = Convert.ToUInt64(traw1);
                        }

                        int sp1 = Convert.ToUInt16(o[nameSP1]);
                        int sp2 = Convert.ToUInt16(o[nameSP2]);

                        ret.OsServicePackVersion = new Version(sp1, sp2);
                        ret.OsServicePackName = Convert.ToString(o[nameSP]);
                        ret.OsName = Convert.ToString(o[nameOsName]);

                        if (!string.IsNullOrEmpty(ret.OsName))
                            ret.OsName = ret.OsName.Trim();

                        try
                        {
                            ret.OsVersion = new Version((string) o[nameOsVersion]);
                        }
                        catch
                        {
                        }

                        break;
                    }
                }

                using(ManagementObjectSearcher search = new ManagementObjectSearcher("Select Name From Win32_Processor"))
                    foreach (ManagementBaseObject o in search.Get())
                    {
                        object rName = o["Name"];
                        if (rName is string)
                            ret.ProcessorName = (string) rName;

                        while (ret.ProcessorName != null && ret.ProcessorName.IndexOf("  ") >= 0)
                            ret.ProcessorName = ret.ProcessorName.Replace("  ", " ");

                        if (!string.IsNullOrEmpty(ret.ProcessorName))
                            break;
                    }

                ret.PhysicalMemory = (int)(mem / 1024L);
                ret.NumberOfProcessors = Environment.ProcessorCount;
                ret.DotNetList = DotNetInfoCollection.Current;

                List<VideoControllerInfo> videos = new List<VideoControllerInfo>();
                string wqlVideo = "Select Availability, Name, AdapterRAM, Status, DriverDate, DriverVersion From Win32_VideoController";
                using (ManagementObjectSearcher search = new ManagementObjectSearcher(wqlVideo))
                    foreach (ManagementBaseObject o in search.Get())
                    {
                        int avail = Convert.ToInt16(o["Availability"]);
                        if (avail != 8 && avail != 9 && avail != 10 && avail != 11)
                        {
                            string
                                name = o["Name"] as string,
                                status = o["Status"] as string,
                                driverVersion = o["DriverVersion"] as string;

                            object ddRaw = o["DriverDate"], arRaw = o["AdapterRAM"];
                            DateTime driverDate;
                            DateTime? dd = null;
                            if (WmiUtils.TryParseWmiDate(ddRaw as string, out driverDate))
                                dd = driverDate;

                            int? ar = arRaw == null ? default(int?) : ((int) Convert.ToUInt64(arRaw)/(1024*1024));

                            VideoControllerInfo v = new VideoControllerInfo(
                                name == null ? null : name.Trim(),
                                driverVersion == null ? null : driverVersion.Trim(),
                                dd,
                                status,
                                ar);

                            videos.Add(v);
                        }
                    }

                ret.VideoControllers = videos.ToArray();

                return ret;

            }
        }

        public override string ToString()
        {
            StringBuilder video = new StringBuilder();
            foreach (VideoControllerInfo info in VideoControllers)
            {
                string item = info.ToString();
                if (!string.IsNullOrEmpty(item))
                    video.Append(video.Length > 0 ? ", " : "").Append(item);
            }

            string ret =
                OsName
                + (string.IsNullOrEmpty(OsServicePackName) ? "" : " " + OsServicePackName)
                + (OsVersion == null ? "" : " Ver " + OsVersion.ToString())
                + ", " + ProcessorName
                + (video.Length > 0 ? ", Video: " + video : "")
                + (PhysicalMemory > 0 ? ", " + PhysicalMemory + " RAM" : "")
                + (", .NET: " + this.DotNetList)
                ;

            return ret;
        }
    }

    [Serializable]
    public class VideoControllerInfo
    {
        public string Name;
        public string Version;
        public DateTime? DriverDate;
        public string Status;
        public int? RAM;

        public VideoControllerInfo()
        {
        }

        public VideoControllerInfo(string name, string version, DateTime? driverDate, string status, int? ram)
        {
            Name = name;
            Version = version;
            DriverDate = driverDate;
            Status = status;
            RAM = ram;
        }

        public bool IsOK
        {
            get { return Status == "OK";  }
        }

        public override string ToString()
        {
            return string.Format(
                "{0}{1}{2}{3}",
                Name,
                string.IsNullOrEmpty(this.Version) ? "" : " " + this.Version,
                this.RAM.HasValue && this.RAM.Value > 0 ? " " + this.RAM.Value + "M" : "",
                string.IsNullOrEmpty(this.Status) ? "" : " (" + this.Status + ")"
                );
        }
    }

    [Serializable]
    public class DotNetInfo
    {
        [XmlIgnore]
        public Version Ver;
        public int ServicePack;
        public string Path;

        public string VersionAsString
        {
            get { return Ver.ToString(); }
            set { Ver = new Version(value); }
        }

        public DotNetInfo()
        {
        }

        public DotNetInfo(Version ver, int servicePack, string path)
        {
            Ver = ver;
            ServicePack = servicePack;
            Path = path;
        }

        public override string ToString()
        {
            return Ver
                   + (ServicePack > 0 ? " SP" + ServicePack : "");
            // + (FullVersion != null ? " ver " + FullVersion.ToString() : "");
        }

        public static string RootPath
        {
            get
            {
                string rootPath = null;

                using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework"))
                {
                    if (key != null)
                    {
                        rootPath = key.GetValue("InstallRoot") as string;
                        if (rootPath != null)
                            rootPath = new DirectoryInfo(rootPath).FullName;
                    }
                }

                if (!Directory.Exists(rootPath))
                {
                    rootPath =
                        global::System.IO.Path.Combine(
                            global::System.IO.Path.GetDirectoryName(new DirectoryInfo(Environment.SystemDirectory).FullName),
                            "Microsoft.NET\\Framework");

                }

                return rootPath;
            }
        }
    }

    [Serializable]
    public class DotNetInfoCollection : Collection<DotNetInfo>
    {
        public static class WellKnownDotNet
        {
            public static readonly string
                Net11 = "1.1.4322",
                Net20 = "2.0.50727",
                Net30 = "3.0",
                Net35 = "3.5";
        }
        
        public int? GetDotNetServicePack(string dotNetVersion)
        {
            foreach (DotNetInfo info in this)
                if (info.ToString() == dotNetVersion)
                    return info.ServicePack;

            return null;
        }

        public static DotNetInfoCollection Current
        {
            get
            {
                string rootPath = DotNetInfo.RootPath;
                DotNetInfoCollection ret = new DotNetInfoCollection();
                using (RegistryKey ndp = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP"))
                {
                    if (ndp != null)
                    {
                        foreach (string sub in new List<string>(ndp.GetSubKeyNames()))
                        {
                            Version v0 = null;
                            if (sub.StartsWith("v"))
                            {

                                try
                                {
                                    v0 = new Version(sub.Substring(1));
                                }
                                catch
                                {
                                }
                            }
                            if (v0 != null)
                            {
                                using (RegistryKey reg = ndp.OpenSubKey(sub))
                                    if (reg != null)
                                    {
                                        try
                                        {
                                            object rawInstall = reg.GetValue("Install");
                                            string rawVersion = reg.GetValue("Version") as string;
                                            object rawSP = reg.GetValue("SP");
                                            if (rawInstall is int && ((int)rawInstall) == 1)
                                            {
                                                int sp = rawSP is int ? (int)rawSP : 0;
                                                string path = Path.Combine(rootPath, sub);
                                                if (Directory.Exists(path))
                                                {
                                                    ret.Add(new DotNetInfo(v0, sp, path));
                                                }
                                            }
                                        }
                                        catch (Exception ex)
                                        {
                                            Debug.WriteLine("Broken .NET " + sub + ". See Details Below" + Environment.NewLine + ex);
                                        }
                                    }
                            }
                        }
                    }
                }

                return ret;
            }
        }


        public override string ToString()
        {
            StringBuilder net = new StringBuilder();
            foreach (DotNetInfo info in this)
            {
                net.Append(net.Length > 0 ? "; " : "").Append(info);
            }

            return net.ToString();


        }
    }
}